package com.cxria.sms.filestore;

import com.cxria.sms.module.Entity;
import com.cxria.sms.persistence.PersistenceException;
import com.cxria.sms.persistence.PersistenceService;

import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.nio.file.DirectoryStream;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.List;
import java.util.Optional;

public class PersistenceServiceImpl implements PersistenceService {

    private final Path dataPath = Paths.get("/home/xiefeihong/data");

    @Override
    public <T extends Entity> List<T> list(Class<T> type) throws PersistenceException {
        List<T> list = new ArrayList<>();
        try(DirectoryStream<Path> paths = Files.newDirectoryStream(this.getEntitiesPath(type), "*.bin")){
            for(Path path : paths){
                list.add(this.readEntity(path));
            }
        } catch (Exception e){
            e.printStackTrace();
            throw new PersistenceException(e);
        }
        return list;
    }

    @Override
    public <T extends Entity> Optional<T> get(Class<T> type, String id) throws PersistenceException {
        try{
            Entity entity = this.readEntity(this.getEntityPath(type, id));
            return (Optional<T>) Optional.of(entity);
        } catch (Exception e){
            e.printStackTrace();
            throw new PersistenceException(e);
        }
    }

    @Override
    public void save(Entity entity) throws PersistenceException {
        try {
            this.saveEntity(entity);
        } catch (IOException e) {
            e.printStackTrace();
            throw new PersistenceException(e);
        }
    }

    @Override
    public <T extends Entity> void delete(Class<T> type, String id) throws PersistenceException {
        try {
            this.deleteEntity(type, id);
        } catch (IOException e) {
            e.printStackTrace();
            throw new PersistenceException(e);
        }
    }


    private Path getEntitiesPath(Class<?> type){
        return dataPath.resolve(type.getSimpleName());
    }

    private Path getEntityPath(Class<?> type, String id){
        return this.getEntitiesPath(type).resolve(String.format("%s.bin", id));
    }

    private <T extends Entity> T readEntity(Path path) throws IOException, ClassNotFoundException {
        try(ObjectInputStream obejctInputStream = new ObjectInputStream(Files.newInputStream(path))){
            return (T) obejctInputStream.readObject();
        }
    }

    private void saveEntity(Entity entity) throws IOException {
        Path path = this.getEntityPath(entity.getClass(), entity.getId());
        try(ObjectOutputStream outputStream = new ObjectOutputStream(Files.newOutputStream(path))){
            outputStream.writeObject(entity);
        }
    }

    private void deleteEntity(Class<?> type, String id) throws IOException {
        Files.deleteIfExists(this.getEntityPath(type, id));
    }

}
